<template>
    <div class="game">
        <div class="game-div">
            <div class="game-min">
                <div class="row" v-for="(row, i) in frame" :key="i">
                    <p
                        class="element"
                        v-for="(col, j) in row"
                        :key="j"
                        :style="{ background: col.bg }"
                    ></p>
                </div>
            </div>
            <div class="game-associate">
                <div class="row" v-for="(row, i) in associate" :key="i">
                    <p
                        class="element"
                        v-for="(col, j) in row"
                        :key="j"
                        :style="{ background: col.bg }"
                    >
                        <!-- {{ col.data }} -->
                    </p>
                </div>
                <div class="info">
                    <div class="score">
                        <p class="title">得分</p>
                        <p class="content">{{ score }}</p>
                    </div>
                    <div class="grade">
                        <p class="title">等级</p>
                        <p class="content">{{ grade }}</p>
                    </div>
                    <div class="diminish">
                        <p class="title">消除次数</p>
                        <p class="content">{{ diminish }}</p>
                    </div>
                    <div class="stop" @click="stopOrStartGame">
                        {{ stopWord }}
                    </div>
                </div>
            </div>
        </div>
        <div class="control">
            <div class="translate" @click="revolveWhileFalling">变换</div>
            <div class="horizontal">
                <div class="left" @click="moveLeft">向左</div>
                <div class="right" @click="moveRight">向右</div>
            </div>
            <div class="down" @click="moveDown">向下</div>
            <div class="confirm">确定</div>
        </div>
    </div>
</template>

<script>
import { color, blockMod, transition } from "@/utils/ykdata";
export default {
    data() {
        return {
            // 游戏空间行列数
            row: 20,
            col: 10,
            // 主屏界面
            frame: [],
            // 副屏界面
            associate: [],
            bg: "#eee",
            // 生成的方块合集
            block: [],
            // 当前方块及旋转角度
            now: {
                blockIdx: 0,
                rotateDeg: 0,
            },
            // 当前方块详细数据
            nowBlock: [],
            // 下一个方块及旋转角度
            next: {
                blockIdx: 0,
                rotateDeg: 0,
            },
            // 下一个方块详细数据
            nextBlock: {},
            // 下落过程中的旋转角度
            currDeg: 0,
            // 自动下落控制器
            fallTimer: null,
            // 下落速度 单位毫秒
            speed: 1000,
            // 游戏暂停
            stop: true,
            stopWord: "开始",
            // 可以消除的行
            eliminateLineList: [],
            // 游戏数据
            score: 0,
            grade: 1,
            diminish: 0,
        };
    },
    mounted() {
        this.gameFrame();
        this.getBlock();
        this.getNext();
        this.init();
    },
    methods: {
        // 暂停与开始
        stopOrStartGame() {
            this.stop = !this.stop;
            if (this.stop) {
                this.stopWord = "开始";
                clearInterval(this.fallTimer);
            } else {
                this.stopWord = "暂停";
                this.autoMoveDown();
            }
        },
        gameFrame() {
            // 主屏
            for (let i = 0; i < this.row; i++) {
                let a = [];
                for (let j = 0; j < this.col; j++) {
                    let b = {
                        // 表明该格是否被占用 0表示没占用
                        data: 0,
                        bg: this.bg,
                    };
                    a.push(b);
                }
                this.frame.push(a);
            }

            // 副屏
            for (let i = 0; i < 4; i++) {
                let a = [];
                for (let j = 0; j < 4; j++) {
                    let b = {
                        data: 0,
                        bg: this.bg,
                    };
                    a.push(b);
                }
                this.associate.push(a);
            }

            // 模拟格子占用
            // this.frame[4][4].bg = "red";
            // this.frame[4][4].data = 1;
        },
        getBlock() {
            this.block = blockMod(color);
        },
        // 随机获取下一个形状
        async getNext() {
            this.next.blockIdx = Math.floor(Math.random() * this.block.length);
            // 4种不同的旋转角度
            this.next.rotateDeg = Math.floor(Math.random() * 4);
        },
        // 渲染当前形状
        init() {
            this.now = JSON.parse(JSON.stringify(this.next));
            this.currDeg = this.next.rotateDeg;

            this.nowBlock = JSON.parse(
                JSON.stringify(this.block[this.now.blockIdx])
            );

            this.renderBlock(this.nowBlock, this.frame, 1);

            // 旋转
            if (this.now.rotateDeg > 0) {
                for (let i = 0; i < this.now.rotateDeg; i++) {
                    this.revolve(this.nowBlock, this.frame, this.now, i);
                }
            }

            this.getNext().then(() => {
                if (this.nextBlock.hasOwnProperty("site")) {
                    this.renderBlock(this.nextBlock, this.associate, 0);
                }

                this.nextBlock = JSON.parse(
                    JSON.stringify(this.block[this.next.blockIdx])
                );
                this.renderBlock(this.nextBlock, this.associate, 1);
                if (this.next.rotateDeg > 0) {
                    for (let i = 0; i < this.next.rotateDeg; i++) {
                        this.revolve(
                            this.nextBlock,
                            this.associate,
                            this.next,
                            i
                        );
                    }
                }
            });
        },
        // 改变背景颜色
        /**
         * @param {*} block 方块信息
         * @param {*} face 主屏/副屏界面
         * @param {*} param 操作控制参数 0擦除 1生成 2掉落
         */
        renderBlock(block, face, param) {
            let site = block.site;
            if (param == 0) {
                // 擦除
                for (let i = 0; i < site.length; i += 2) {
                    face[site[i]][site[i + 1]].bg = this.bg;
                }
            } else if (param == 1) {
                // 生成
                for (let i = 0; i < site.length; i += 2) {
                    face[site[i]][site[i + 1]].bg = block.color;
                }
            } else if (param == 2) {
                // 确定掉落
                for (let i = 0; i < site.length; i += 2) {
                    face[site[i]][site[i + 1]].data = 1;
                }
            }
        },
        // 旋转方块
        /**
         * @param {*} block 当前方块
         * @param {*} face 渲染位置
         * @param {*} target 渲染对象是现在的还是下一个
         * @param {*} deg 旋转角度
         */
        revolve(block, face, target, deg) {
            // 清空
            this.renderBlock(block, face, 0);
            // 第一个方块基坐标
            const x = block.site[0];
            const y = block.site[1];
            for (let i = 0; i < block.site.length; i += 2) {
                let memo = block.site[i];
                block.site[i] =
                    block.site[i + 1] -
                    y +
                    x +
                    transition[target.blockIdx][deg].x;
                block.site[i + 1] =
                    -(memo - x) + y + transition[target.blockIdx][deg].y;
            }
            deg++;
            if (deg == 4) {
                deg = 0;
            }
            this.renderBlock(block, face, 1);
        },
        // 下落时旋转方块
        revolveWhileFalling() {
            // 不直接修改nowBlock
            const tempBlock = JSON.parse(JSON.stringify(this.nowBlock));

            // 清空
            this.renderBlock(this.nowBlock, this.frame, 0);

            // 第一个方块基坐标
            const x = tempBlock.site[0];
            const y = tempBlock.site[1];
            let allowRotate = true;
            for (let i = 0; i < tempBlock.site.length; i += 2) {
                let memo = tempBlock.site[i];
                // 行
                tempBlock.site[i] =
                    tempBlock.site[i + 1] -
                    y +
                    x +
                    transition[this.now.blockIdx][this.currDeg].x;
                // 列
                tempBlock.site[i + 1] =
                    -(memo - x) +
                    y +
                    transition[this.now.blockIdx][this.currDeg].y;

                // 旋转结果检测 越界+碰撞
                if (
                    tempBlock.site[i + 1] < 0 ||
                    tempBlock.site[i + 1] >= this.col ||
                    tempBlock.site[i] >= this.row ||
                    this.frame[tempBlock.site[i]][tempBlock.site[i + 1]].data >
                        0
                ) {
                    allowRotate = false;
                }
            }
            if (allowRotate) {
                this.currDeg++;
                if (this.currDeg == 4) {
                    this.currDeg = 0;
                }
                this.nowBlock = tempBlock;
            }

            this.renderBlock(this.nowBlock, this.frame, 1);
        },
        // 自动下落
        autoMoveDown() {
            this.fallTimer = setInterval(() => {
                this.moveDown();
            }, this.speed);
        },
        // 方块下移
        moveDown() {
            if (this.canMove(3)) {
                // 清理原先方块
                this.renderBlock(this.nowBlock, this.frame, 0);
                for (let i = 0; i < this.nowBlock.site.length; i += 2) {
                    this.nowBlock.site[i]++;
                }
                // 重新渲染
                this.renderBlock(this.nowBlock, this.frame, 1);
            } else {
                // 无法下落
                // 确认固定
                this.renderBlock(this.nowBlock, this.frame, 2);
                // 消除判断
                this.allowEliminateLine();
            }
        },
        // 方块左移
        moveLeft() {
            if (this.canMove(2)) {
                // 清理原先方块
                this.renderBlock(this.nowBlock, this.frame, 0);
                for (let i = 0; i < this.nowBlock.site.length; i += 2) {
                    this.nowBlock.site[i + 1]--;
                }
                // 重新渲染
                this.renderBlock(this.nowBlock, this.frame, 1);
            }
        },
        // 方块右移
        moveRight() {
            if (this.canMove(1)) {
                // 清理原先方块
                this.renderBlock(this.nowBlock, this.frame, 0);
                for (let i = 0; i < this.nowBlock.site.length; i += 2) {
                    this.nowBlock.site[i + 1]++;
                }
                // 重新渲染
                this.renderBlock(this.nowBlock, this.frame, 1);
            }
        },
        // 判断方块能否移动 越界判断
        canMove(e) {
            // e=1右 e=2左 e=3下 e=4旋转
            let site = this.nowBlock.site;
            let collide = 0;
            switch (e) {
                case 1:
                    for (let i = 0; i < site.length; i += 2) {
                        // 越界判断
                        if (site[i + 1] >= this.col - 1) {
                            return false;
                        }
                        // 碰撞判断
                        collide += this.frame[site[i]][site[i + 1] + 1].data;
                    }
                    if (collide > 0) {
                        return false;
                    }
                    return true;
                case 2:
                    for (let i = 0; i < site.length; i += 2) {
                        if (site[i + 1] <= 0) {
                            return false;
                        }
                        collide += this.frame[site[i]][site[i + 1] - 1].data;
                    }
                    if (collide > 0) {
                        return false;
                    }
                    return true;
                case 3:
                    for (let i = 0; i < site.length; i += 2) {
                        if (site[i] >= this.row - 1) {
                            return false;
                        }
                        // 碰撞判断
                        // 查询当前点下一行同列的点的data值
                        collide += this.frame[site[i] + 1][site[i + 1]].data;
                    }
                    if (collide > 0) {
                        return false;
                    }
                    return true;
            }
        },
        allowEliminateLine() {
            for (let i = 0; i < this.row; i++) {
                let count = 0;
                for (let j = 0; j < this.col; j++) {
                    if (this.frame[i][j].data == 1) {
                        count++;
                    }
                }
                if (count == this.col) {
                    // 整行满了可以消除
                    this.eliminateLineList.push(i);
                }
            }
            const listLen = this.eliminateLineList.length;
            if (this.eliminateLineList.length > 0) {
                for (let i = 0; i < this.eliminateLineList.length; i++) {
                    let j = 0;
                    let timer = setInterval(() => {
                        this.frame[this.eliminateLineList[i]][j] = {
                            bg: this.bg,
                            data: 0,
                        };
                        j++;
                        if (j == this.col) {
                            clearInterval(timer);
                        }
                    }, 20);
                }
                setTimeout(() => {
                    // 悬空方块下移
                    for (let i = this.row - 1; i >= 0; i--) {
                        // 记录需要下移的行数
                        let count = 0;
                        for (
                            let j = 0;
                            j < this.eliminateLineList.length;
                            j++
                        ) {
                            // 有被移除的行在当前行i的下面时就需要下移一行
                            if (this.eliminateLineList[j] > i) {
                                count++;
                            }
                        }
                        if (count > 0) {
                            for (let k = 0; k < this.col; k++) {
                                if (this.frame[i][k].data == 1) {
                                    // 下移操作
                                    this.frame[i + count][k] = this.frame[i][k];
                                    // 清除原来
                                    this.frame[i][k] = {
                                        data: 0,
                                        bg: this.bg,
                                    };
                                }
                            }
                        }
                    }
                    // 清除删除的记录
                    this.eliminateLineList = [];
                    // 生成下一个
                    this.init();
                }, 20 * this.col);

                // 游戏数据处理
                this.diminish++;
                this.grade = Math.floor(this.diminish / 10) + 1;
                this.score +=
                    ((listLen * (listLen + 1)) / 2) * 100 * this.grade;
                if (this.grade < 21) {
                    this.speed = 1000 - (this.grade - 1) * 40;
                } else {
                    this.speed = 240;
                }
            } else {
                // 生成下一个
                this.init();
            }
        },
    },
};
</script>

<style lang="less" scoped>
.game {
    display: flex;
    flex-direction: column;
    .game-div {
        display: flex;
        .game-min {
            margin-right: 20px;
            .row {
                display: flex;
                .element {
                    width: 20px;
                    height: 20px;
                    line-height: 20px;
                    text-align: center;
                    margin: 1px 1px 0 0;
                }
            }
        }
        .game-associate {
            .row {
                display: flex;
                .element {
                    width: 20px;
                    height: 20px;
                    line-height: 20px;
                    text-align: center;
                    margin: 1px 1px 0 0;
                }
            }
            .info {
                margin-top: 20px;
                user-select: none;
                .score,
                .grade,
                .diminish {
                    background-color: #eee;
                    border: 1px solid #aaa;
                    border-radius: 10px;
                    text-align: center;
                    margin-bottom: 14px;
                }

                .stop {
                    background: linear-gradient(to right, #c3e7fe, #3491fa);
                    border: 1px solid #c3e7fe;
                    color: #114ba3;
                    transition: box-shadow .3s;
                    border-radius: 10px;
                    text-align: center;
                    height: 40px;
                    line-height: 40px;
                    margin-bottom: 10px;
                    cursor: pointer;
                }
                .stop:hover {
                    box-shadow: 2px 2px 6px #bbb;
                }
            }
        }
    }
    .control {
        user-select: none;
        margin: 14px 0 0 24px;
        .translate,
        .left,
        .right,
        .down,
        .confirm {
            background-color: #eee;
            width: 160px;
            height: 30px;
            line-height: 30px;
            text-align: center;
            border: 1px solid #aaa;
            border-radius: 10px;
            margin-bottom: 10px;
            cursor: pointer;
        }
        .left,
        .right {
            width: 70px;
        }
        .horizontal {
            width: 160px;
            display: flex;
            justify-content: space-between;
        }
    }
}
</style>

